﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;

namespace LiteFramework
{
    [Serializable]
    public class HashtableSerializationProxy : ICollection, ISerializable
    {
        [NonSerialized]
        private Hashtable _hashtable;

        [NonSerialized]
        private IDictionaryEnumerator _enumerator = null;

        [NonSerialized]
        private int _position = -1;

        [NonSerialized]
        internal List<object> NonSerializedKeys = null;

        public HashtableSerializationProxy()
            : this(new Hashtable())
        {
        }

        public HashtableSerializationProxy(Hashtable hashtable)
        {
            _hashtable = hashtable;
        }

        public HashtableSerializationProxy(SerializationInfo info, StreamingContext context)
            : this()
        {
            if (info != null)
            {
                object[] keys = (object[])info.GetValue("_ElementKeys", typeof(object[]));
                object[] values = (object[])info.GetValue("_ElementValues", typeof(object[]));

                if (keys.Length != values.Length)
                    throw new SerializationException("键值对的个数不匹配。");

                lock (_hashtable.SyncRoot)
                {
                    for (int i = 0; i < keys.Length; i++)
                    {
                        object key = keys[i];
                        object value = values[i];

                        if (key is String && value is String)
                        {
                            string skey = (string)key;
                            string svalue = (string)value;

                            if (skey.EndsWith("$FROZEN"))
                            {
                                _hashtable[skey.Remove(skey.Length - 7)] = this.FromBase64(svalue);
                            }
                            else
                            {
                                _hashtable[skey] = svalue;
                            }
                        }
                        else
                        {
                            _hashtable[key] = value;
                        }
                    }
                }
            }
        }

        #region Public

        // Deserialization: XmlSerializer uses this one to write content back
        public int Add(DictionaryEntry entry)
        {
            this[entry.Key] = entry.Value;
            return _hashtable.Count;
        }

        // Serialization: XmlSerializer uses this one to get one item at the time
        public DictionaryEntry this[int index]
        {
            get
            {
                if (_enumerator == null)  // lazy initialization
                    _enumerator = (IDictionaryEnumerator)this.GetEnumerator();

                DictionaryEntry entry;

                lock (this)
                {
                    if (index < _position)
                    {
                        _enumerator.Reset();
                        _position = -1;
                    }

                    while (_position < index)
                    {
                        _enumerator.MoveNext();
                        _position++;
                    }

                    entry = _enumerator.Entry;
                }

                if (entry.Value == DBNull.Value)   // DBNull.Value 无法序列化：System.InvalidOperationException: 生成 XML 文档时出错。 ---> System.InvalidOperationException: 不应是类型 System.DBNull。
                {
                    entry.Value = null;
                }

                return entry;
            }
            set
            {
                throw new NotSupportedException();
            }
        }
        #endregion

        #region Internal

        internal ICollection Keys
        {
            get
            {
                return _hashtable.Keys;
            }
        }

        internal ICollection Values
        {
            get
            {
                return _hashtable.Values;
            }
        }

        internal object this[object key]
        {
            get
            {
                return _hashtable[key];
            }
            set
            {
                if (_hashtable.ContainsKey(key))
                {
                    _hashtable[key] = value;
                }
                else
                {
                    _hashtable.Add(key, value);
                    this.Reset();
                }
            }
        }

        internal void Remove(object key)
        {
            _hashtable.Remove(key);
            this.Reset();
        }

        internal void Clear()
        {
            _hashtable.Clear();
            this.Reset();
        }

        internal bool ContainsKey(object key)
        {
            return _hashtable.ContainsKey(key);
        }

        #endregion

        #region Private

        private void Reset()
        {
            _enumerator = null;
            _position = -1;
        }

        /// <summary>
        /// 将字符串转换为等效的8位无符号整数数组，并反序列化为对象图形
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public object FromBase64(string data)
        {
            if (!string.IsNullOrEmpty(data))
            {
                return this.Deserialize(Convert.FromBase64String(data));
            }
            return null;
        }
        /// <summary>
        /// 将obj转换为等效的字符串表示形式
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public string ToBase64(object obj)
        {
            if (obj != null)
            {
                byte[] inArray = this.Serialize(obj);
                if ((inArray != null) && (inArray.Length != 0))
                {
                    return Convert.ToBase64String(inArray);
                }
            }
            return null;
        }


        public object Deserialize(byte[] buffer)
        {
            if ((buffer == null) || (buffer.Length == 0))
            {
                return null;
            }
            using (MemoryStream stream = new MemoryStream(buffer))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                return formatter.Deserialize(stream);
            }
        }

        public byte[] Serialize(object obj)
        {
            if (obj == null)
            {
                return null;
            }
            using (MemoryStream stream = new MemoryStream())
            {
                new BinaryFormatter().Serialize(stream, obj);
                stream.Position = 0L;
                return stream.ToArray();
            }
        }

        #endregion

        #region ICollection 成员

        public int Count
        {
            get { return _hashtable.Count; }
        }

        public bool IsSynchronized
        {
            get { return _hashtable.IsSynchronized; }
        }

        public object SyncRoot
        {
            get { return _hashtable.SyncRoot; }
        }

        public void CopyTo(Array array, int index)
        {
            _hashtable.CopyTo(array, index);
        }

        #endregion

        #region IEnumerable 成员

        public IEnumerator GetEnumerator()
        {
            if (_hashtable == null)
                _hashtable = new Hashtable();

            return _hashtable.GetEnumerator();
        }

        #endregion

        #region ISerializable 成员

        [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
                throw new ArgumentNullException("info");

            List<object> keys = new List<object>();
            List<object> values = new List<object>();

            lock (_hashtable.SyncRoot)
            {
                foreach (object key in _hashtable.Keys)
                {
                    if (this.NonSerializedKeys != null && this.NonSerializedKeys.IndexOf(key) != -1)
                        continue;

                    object value = _hashtable[key];

                    //if (value is NHibernate.Collection.PersistentBag)
                    //{
                    //    NHibernate.Collection.PersistentBag pb = (NHibernate.Collection.PersistentBag)value;

                    //    Object[] os = new Object[pb.Count];
                    //    pb.CopyTo(os, 0);

                    //    value = new List<object>(os);
                    //}

                    //if (key is String &&
                    //    (value is Bag ||
                    //    value is Bag[] ||
                    //    value is CodetableEntry ||
                    //    ((value is IList) && value.GetType().IsGenericType)))
                    //{
                    //    keys.Add(key + "$FROZEN");
                    //    values.Add(this.ToBase64(value));
                    //}
                    //else
                    {
                        keys.Add(key);
                        values.Add(value);
                    }
                }
            }

            info.AddValue("_ElementKeys", keys.ToArray(), typeof(object[]));
            info.AddValue("_ElementValues", values.ToArray(), typeof(object[]));
        }

        #endregion
    }
}
